// cf-220b
// 题意：给定n(<=10^5)个正整数ai(1<=ai<=10^9)，有m(<=10^5)个询问，每次询问
//       一段区间[l, r]，回答这段区间内数x的个数，满足x的出现次数正好为
//       x。
//
// 题解：首先很直白的莫队算法。其实还有个也是O(n sqrt(n))的分块算法，就是
//       只考虑出现次数大于x的数x，然后类似前缀和可以搞。
//
//       但是这道题还有O(n log(n))的算法。
//       假设当前数x从第一个数迭代到第n个数，我们维护一个数组d[i]，第d[i]
//       表示从第i个数到当前做到的数的这段区间内，第i个数a[i]正好出现了
//       a[i]次。我们还需要维护个向量p，表示当前数x之前所有出现的位置，
//       如果|p|>=x，那么给d[p[|p|-x]]加上1, 表示当前x开始前x位置的那个
//       x如果在这区间内对区间的贡献+1, 如果|p|>x，还需要给d[p[|p|-x-1]]
//       这个地方减去2, 因为原来是1然后要去掉它的贡献减去1, 另外如果当前
//       区间的l端点包括这个位置，那么这个区间就有超过x个x，所以要减去
//       刚刚加给p[|p|-x]的贡献，所以一共是减2.
//       然后如果做到x，假设其位置是r，就可以计算所有询问右端点是x的答案，
//       答案就是 d[l]+d[l+1]+d[l+2]+...+d[r]。
//       这是个离线算法。
//
// run: $exec < input
// opt: 0
// flag: -g
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>

struct query { int l, r, id; };
std::vector<query> q;

int const maxn = 100007;
int const size = 317;
std::unordered_map<int, int> count;
int a[maxn];
int n, m;
long long ans[maxn];
long long cur_ans = 0;

bool mo_cmp(query const & a, query const & b)
{
	if (a.l / size != b.l / size) return a.l / size < b.l / size;
	else return (a.r < b.r) ^ ((a.l / size) & 1); // faster
//	else return a.r < b.r;
}

void mo_add(int x)
{
	if (!x) return;
	if (count[x]++ == x) cur_ans--;
	if (count[x] == x) cur_ans++;
}

void mo_remove(int x)
{
	if (!x) return;
	if (count[x]-- == x) cur_ans--;
	if (count[x] == x) cur_ans++;
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin >> n >> m;
	for (int i = 1; i <= n; i++) std::cin >> a[i];
	q.resize(m);
	for (int i = 0; i < m; i++) {
		std::cin >> q[i].l >> q[i].r;
		q[i].id = i;
	}
	std::sort(q.begin(), q.end(), mo_cmp);
	int l = 0, r = 0;
	for (auto i : q) {
		while (l < i.l) mo_remove(a[l++]);
		while (l > i.l) mo_add(a[--l]);
		while (r > i.r) mo_remove(a[r--]);
		while (r < i.r) mo_add(a[++r]);
		ans[i.id] = cur_ans;
	}
	for (int i = 0; i < m; i++)
		std::cout << ans[i] << '\n';

}

